package wci.backend.compiler;

import static wci.intermediate.symtabimpl.DefinitionImpl.VALUE_PARM;
import static wci.intermediate.symtabimpl.DefinitionImpl.VARIABLE;
import static wci.intermediate.symtabimpl.DefinitionImpl.VAR_PARM;

import java.util.ArrayList;

import wci.backend.compiler.CodeGenerator.MethodType;
import wci.frontend.ASTmethodBody;
import wci.frontend.Node;
import wci.frontend.eREMParserConstants;
import wci.intermediate.Definition;
import wci.intermediate.ICode;
import wci.intermediate.PclParserVisitorAdapter;
import wci.intermediate.SymTabEntry;
import wci.intermediate.SymTabStack;
import wci.intermediate.TypeSpec;
import wci.intermediate.icodeimpl.ICodeKeyImpl;
import static wci.intermediate.symtabimpl.SymTabKeyImpl.*;
import wci.frontend.*;
import wci.intermediate.*;
import wci.intermediate.symtabimpl.Predefined;
import wci.backend.*;

import static wci.intermediate.symtabimpl.SymTabKeyImpl.*;
import static wci.intermediate.symtabimpl.DefinitionImpl.*;

public class MethodVisitor extends PclParserVisitorAdapter implements eREMParserConstants {

	/* (non-Javadoc)
	 * @see wci.intermediate.PclParserVisitorAdapter#visit(wci.frontend.ASTmethodBody, java.lang.Object)
	 */
	@Override
	public Object visit(ASTmethodBody node, Object data) {
		/*
		 * Chris D: The last child is the compound statement representing
		 * the actual statements inside this body.  All other nodes
		 * are param nodes.
		 */
		
		/*
		 * TODO: Write some code to generate the jasmin for defining
		 * a function, declaring it's parameters.
		 */
		//System.out.println("**PROCESSING METHOD NAMED**:" + node.getAttribute(ICodeKeyImpl.ID));
		
		// TODO: Create a CodeGeneratorVisitor and visit the last child.
		
		// TODO: Close the function out with the proper jasmin code.
		
		 //CodeGenerator.objectFile.println(".method public  run([Ljava/lang/String;)V");
	        
	      //  CodeGenerator.objectFile.println("	getstatic java/lang/System/out Ljava/io/PrintStream;");
	        
		 //node.getAttribute(ROUTINE_SYMTAB);
		
		
		
		SymTabEntry nodeEntry = (SymTabEntry)node.getAttribute(ICodeKeyImpl.ID);
		SymTab nodeLocalStack = (SymTab) nodeEntry.getAttribute(ROUTINE_SYMTAB);
		ArrayList<SymTabEntry> locals = nodeLocalStack.sortedEntries();
		
		String methodName = nodeEntry.getName();
		StringBuilder methodDefinition = new StringBuilder();
		
		methodDefinition.append("\n\n.method public " + methodName+"("); //Ljava/lang/Object;)V");
		for (SymTabEntry id : locals) {
			methodDefinition.append("Ljava/lang/Object;");
		}
		methodDefinition.append(")V");
		
		
		CodeGenerator.objectFile.println(methodDefinition.toString());
		
	        //Generate the .var lines.
	        for (SymTabEntry id : locals) {
	            Definition defn = id.getDefinition();
	            
	            if ((defn == VARIABLE) || (defn == VAR_PARM) || (defn == VALUE_PARM)) {
	                String localName = id.getName();
	                int localIndex =  CodeGenerator.GetVarIndex(id.getIndex(), MethodType.Instance);
	                TypeSpec type = id.getTypeSpec();                
	                CodeGenerator.objectFile.println(".var " + localIndex + " is " +
	                                   localName + " " +  CodeGenerator.GetTypeCode(type));
	            }
	        }
	        
	        CodeGenerator.objectFile.println();
	        CodeGenerator.objectFile.flush();

	        CodeInfo ci = new CodeInfo();
	        ci.type = MethodType.Instance;
	        // Visit the parse tree nodes to generate code.
	        MethodBodyGenerator codeVisitor = new MethodBodyGenerator();
	        //Node rootNode =  CodeGenerator.iCode.getRoot();
	        node.jjtAccept(codeVisitor, ci);

	        //CodeGenerator.objectFile.println("invokenonvirtual " + programName + "/show(Ljava/lang/String;)V");
	        
	        //CodeGenerator.objectFile.println("	invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V");
	        
	        // Generate the footer code.
	        CodeGenerator.objectFile.println("    return");
	        CodeGenerator.objectFile.println();
	        CodeGenerator.objectFile.println(".limit locals " + 10);//localsCount);
	        CodeGenerator.objectFile.println(".limit stack  " +  CodeGenerator.STACK_LIMIT);
	        CodeGenerator.objectFile.println(".end method\n");
	        CodeGenerator.objectFile.flush();
		
		
		
		return super.visit(node, data);
	}
	
	/**
	 * This inner class is reponsible for properly generating the method body code.
	 * The CodeGeneratorVisitor is design to ignore it, so it's dealt with here.
	 * @author Chris Douglass
	 */
	private class MethodBodyGenerator extends CodeGeneratorVisitor {
		@Override
		public Object visit(ASTmethodBody node, Object data) {
			// TODO Auto-generated method stub
			return node.childrenAccept(this, data);
		}
	}
	
}
